﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Game.geodata;
using Game.model;
using Game.utils;
using java.lang;
using java.util;
using java.util.concurrent;
using lineage2.commons.collections;
using lineage2.commons.lang.reference;
using lineage2.commons.math.random;
using lineage2.commons.threading;
using lineage2.commons.util;
using org.slf4j;
using Math = java.lang.Math;

namespace Game.ai
{
    public class DefaultAI:CharacterAI
    {
        protected static Logger _log = LoggerFactory.getLogger(typeof (DefaultAI));
        public  enum TaskType
        {
            /**
             * Field MOVE.
             */
            MOVE,
            /**
             * Field ATTACK.
             */
            ATTACK,
            /**
             * Field CAST.
             */
            CAST,
            /**
             * Field BUFF.
             */
            BUFF
        }
        public static int TaskDefaultWeight = 10000;
        public  class Task
	    {
		    /**
		     * Field type.
		     */
		    public TaskType type;
		    /**
		     * Field skill.
		     */
		    public Skill skill;
		    /**
		     * Field target.
		     */
		    public HardReference target;
		    /**
		     * Field loc.
		     */
		    public Location loc;
		    /**
		     * Field pathfind.
		     */
		    public bool pathfind;
		    /**
		     * Field weight.
		     */
		    public int weight = TaskDefaultWeight;
	    }
        protected void addTaskCast(Creature target, Skill skill)
        {
            var task = new Task {type = TaskType.CAST, target = target.getRef(), skill = skill};
            _tasks.add(task);
            _def_think = true;
        }
        protected void addTaskBuff(Creature target, Skill skill)
        {
            var task = new Task {type = TaskType.BUFF, target = target.getRef(), skill = skill};
            _tasks.add(task);
            _def_think = true;
        }
        public void addTaskAttack(Creature target)
        {
            var task = new Task {type = TaskType.ATTACK, target = target.getRef()};
            _tasks.add(task);
            _def_think = true;
        }
        protected void addTaskAttack(Creature target, Skill skill, int weight)
        {
            var task = new Task
                {
                    type = skill.isOffensive() ? TaskType.CAST : TaskType.BUFF,
                    target = target.getRef(),
                    skill = skill,
                    weight = weight
                };
            _tasks.add(task);
            _def_think = true;
        }
        public void addTaskMove(Location loc, bool pathfind)
        {
            var task = new Task {type = TaskType.MOVE, loc = loc, pathfind = pathfind};
            _tasks.add(task);
            _def_think = true;
        }
        protected void addTaskMove(int locX, int locY, int locZ, bool pathfind)
        {
            addTaskMove(new Location(locX, locY, locZ), pathfind);
        }
        private class TaskComparator:Comparator
        {
            private static Comparator instance = new TaskComparator();
		
		    /**
		     * Method getInstance.
		     * @return Comparator<Task>
		     */
		    public static Comparator getInstance()
		    {
			    return instance;
		    }
            public int compare(Task o1, Task o2)
            {
                if ((o1 == null) || (o2 == null))
                {
                    return 0;
                }
                return o2.weight - o1.weight;
            }
            
        }
        protected class Teleport:RunnableImpl
        {
            Location _destination;
            public Teleport(Location destination)
            {
                _destination = destination;
            }
            public override void runImpl()
            {
                NpcInstance actor = getActor();
                if (actor != null)
                {
                    actor.teleToLocation(_destination);
                }
            }
        }
        protected class RunningTask:RunnableImpl
        {
            public override void runImpl()
            {
                NpcInstance actor = getActor();
                if (actor != null)
                {
                    actor.setRunning();
                }
                _runningTask = null;
            }
        }
        protected class MadnessTask:RunnableImpl
        {
            public override void runImpl()
            {
                NpcInstance actor = getActor();
                if (actor != null)
                {
                    actor.stopConfused();
                }
                _madnessTask = null;
            }
        }
        protected class NearestTargetComparator:Comparator
        {
            private Creature actor;
            public NearestTargetComparator(Creature actor)
            {
                this.actor = actor;
            }
            public int compare(Creature o1, Creature o2)
            {
                double diff = actor.getDistance3D(o1) - actor.getDistance3D(o2);
                if (diff < 0)
                {
                    return -1;
                }
                return diff > 0 ? 1 : 0;
            }
        }
        protected long AI_TASK_ATTACK_DELAY = Config.AI_TASK_ATTACK_DELAY;
	    /**
	     * Field AI_TASK_ACTIVE_DELAY.
	     */
	    protected long AI_TASK_ACTIVE_DELAY = Config.AI_TASK_ACTIVE_DELAY;
	    /**
	     * Field AI_TASK_DELAY_CURRENT.
	     */
	    protected long AI_TASK_DELAY_CURRENT;
	    /**
	     * Field MAX_PURSUE_RANGE.
	     */
	    protected int MAX_PURSUE_RANGE;
	    /**
	     * Field _aiTask.
	     */
	    protected ScheduledFuture _aiTask;
	    /**
	     * Field _runningTask.
	     */
	    protected ScheduledFuture _runningTask;
	    /**
	     * Field _madnessTask.
	     */
	    public ScheduledFuture _madnessTask;
	    /**
	     * Field _thinking.
	     */
	    private bool _thinking = false;
	    /**
	     * Field _def_think.
	     */
	    protected bool _def_think = false;
	    /**
	     * Field _globalAggro.
	     */
	    protected long _globalAggro;
	    /**
	     * Field _randomAnimationEnd.
	     */
	    protected long _randomAnimationEnd;
	    /**
	     * Field _pathfindFails.
	     */
	    protected int _pathfindFails;
	    /**
	     * Field _tasks.
	     */
	    protected  NavigableSet _tasks = new ConcurrentSkipListSet(TaskComparator.getInstance());
	    /**
	     * Field _stunSkills. Field _buffSkills. Field _healSkills. Field _debuffSkills. Field _dotSkills. Field _damSkills.
	     */
	    protected Skill[] _damSkills, _dotSkills, _debuffSkills, _healSkills, _buffSkills, _stunSkills;
	    /**
	     * Field _lastActiveCheck.
	     */
	    protected long _lastActiveCheck;
	    /**
	     * Field _checkAggroTimestamp.
	     */
	    protected long _checkAggroTimestamp = 0;
	    /**
	     * Field _attackTimeout.
	     */
	    protected long _attackTimeout;
	    /**
	     * Field _lastFactionNotifyTime.
	     */
	    protected long _lastFactionNotifyTime = 0;
	    /**
	     * Field _minFactionNotifyInterval.
	     */
	    protected long _minFactionNotifyInterval = 10000;

        /**
	     * Field _nearestTargetComparator.
	     */
	    protected Comparator _nearestTargetComparator;
        public DefaultAI(NpcInstance actor):base(actor)
        {
            AI_TASK_DELAY_CURRENT = AI_TASK_ACTIVE_DELAY;
            //;
            setAttackTimeout(Long.MAX_VALUE);
            NpcInstance npc = getActor();
            _damSkills = npc.getTemplate().getDamageSkills();
            _dotSkills = npc.getTemplate().getDotSkills();
            _debuffSkills = npc.getTemplate().getDebuffSkills();
            _buffSkills = npc.getTemplate().getBuffSkills();
            _stunSkills = npc.getTemplate().getStunSkills();
            _healSkills = npc.getTemplate().getHealSkills();
            _nearestTargetComparator = new NearestTargetComparator(actor);
            MAX_PURSUE_RANGE = actor.getParameter("MaxPursueRange", actor.isRaid() ? Config.MAX_PURSUE_RANGE_RAID : npc.isUnderground() ? Config.MAX_PURSUE_UNDERGROUND_RANGE : Config.MAX_PURSUE_RANGE);
            _minFactionNotifyInterval = actor.getParameter("FactionNotifyInterval", 10000);
        }
        public override void runImpl()
        {
            if (_aiTask == null)
            {
                return;
            }
            if (!isGlobalAI() && ((java.lang.System.currentTimeMillis() - _lastActiveCheck) > 60000L))
            {
                _lastActiveCheck = java.lang.System.currentTimeMillis();
                NpcInstance actor = getActor();
                WorldRegion region = actor == null ? null : actor.getCurrentRegion();
                if ((region == null) || !region.isActive())
                {
                    stopAITask();
                    return;
                }
            }
            onEvtThink();
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public override void startAITask()
	    {
		    if (_aiTask == null)
		    {
			    AI_TASK_DELAY_CURRENT = AI_TASK_ACTIVE_DELAY;
			    _aiTask = AiTaskManager.getInstance().scheduleAtFixedRate(this, 0L, AI_TASK_DELAY_CURRENT);
		    }
	    }
        [MethodImpl(MethodImplOptions.Synchronized)]
        protected void switchAITask(long NEW_DELAY)
	    {
		    if (_aiTask == null)
		    {
			    return;
		    }
		    if (AI_TASK_DELAY_CURRENT != NEW_DELAY)
		    {
			    _aiTask.cancel(false);
			    AI_TASK_DELAY_CURRENT = NEW_DELAY;
			    _aiTask = AiTaskManager.getInstance().scheduleAtFixedRate(this, 0L, AI_TASK_DELAY_CURRENT);
		    }
	    }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public override void stopAITask()
	    {
		    if (_aiTask != null)
		    {
			    _aiTask.cancel(false);
			    _aiTask = null;
		    }
	    }
        protected bool canSeeInSilentMove(Playable target)
        {
            if (getActor().getParameter("canSeeInSilentMove", false))
            {
                return true;
            }
            return !target.isSilentMoving();
        }
        protected bool canSeeInHide(Playable target)
        {
            if (getActor().getParameter("canSeeInHide", false))
            {
                return true;
            }
            return !target.isInvisible();
        }
        protected virtual bool checkAggression(Creature target)
        {
            NpcInstance actor = getActor();
            if ((getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !isGlobalAggro())
            {
                return false;
            }
            if (target.isAlikeDead())
            {
                return false;
            }
            if (target.isNpc() && target.isInvul())
            {
                return false;
            }
            if (target.isPlayable())
            {
                if (!canSeeInSilentMove((Playable)target))
                {
                    return false;
                }
                if (!canSeeInHide((Playable)target))
                {
                    return false;
                }
                if (actor.getFaction().getName().equalsIgnoreCase("varka_silenos_clan") && (target.getPlayer().getVarka() > 0))
                {
                    return false;
                }
                if (actor.getFaction().getName().equalsIgnoreCase("ketra_orc_clan") && (target.getPlayer().getKetra() > 0))
                {
                    return false;
                }
                if (target.isPlayer() && ((Player)target).isGM() && target.isInvisible())
                {
                    return false;
                }
                if (((Playable)target).getNonAggroTime() > java.lang.System.currentTimeMillis())
                {
                    return false;
                }
                if (target.isPlayer() && !target.getPlayer().isActive())
                {
                    return false;
                }
                if (actor.isMonster() && target.isInZonePeace())
                {
                    return false;
                }
            }
            AggroList.AggroInfo ai = actor.getAggroList().get(target);
            if ((ai != null) && (ai.hate > 0))
            {
                if (!target.isInRangeZ(actor.getSpawnedLoc(), MAX_PURSUE_RANGE))
                {
                    return false;
                }
            }
            else if (!actor.isAggressive() || !target.isInRangeZ(actor.getSpawnedLoc(), actor.getAggroRange()))
            {
                return false;
            }
            if (!canAttackCharacter(target))
            {
                return false;
            }
            if (!GeoEngine.canSeeTarget(actor, target, false))
            {
                return false;
            }
            actor.getAggroList().addDamageHate(target, 0, 2);
            if ((target.isServitor() || target.isPet()))
            {
                actor.getAggroList().addDamageHate(target.getPlayer(), 0, 1);
            }
            startRunningTask(AI_TASK_ATTACK_DELAY);
            setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
            return true;
        }
        protected void setIsInRandomAnimation(long time)
        {
            _randomAnimationEnd = System.currentTimeMillis() + time;
        }
        protected bool randomAnimation()
        {
            NpcInstance actor = getActor();
            if (actor.getParameter("noRandomAnimation", false))
            {
                return false;
            }
            if (actor.hasRandomAnimation() && !actor.isActionsDisabled() && !actor.isMoving && !actor.isInCombat() && Rnd.chance(Config.RND_ANIMATION_RATE))
            {
                setIsInRandomAnimation(3000);
                actor.onRandomAnimation();
                return true;
            }
            return false;
        }
        protected virtual bool randomWalk()
        {
            NpcInstance actor = getActor();
            if (actor.getParameter("noRandomWalk", false))
            {
                return false;
            }
            return !actor.isMoving && maybeMoveToHome();
        }
        protected virtual bool thinkActive()
	    {
		    NpcInstance actor = getActor();
		    if (actor.isActionsDisabled())
		    {
			    return true;
		    }
		    if (_randomAnimationEnd > java.lang.System.currentTimeMillis())
		    {
			    return true;
		    }
		    if (_def_think)
		    {
			    if (doTask())
			    {
				    clearTasks();
			    }
			    return true;
		    }
		    var now = java.lang.System.currentTimeMillis();
		    if ((now - _checkAggroTimestamp) > Config.AGGRO_CHECK_INTERVAL)
		    {
			    _checkAggroTimestamp = now;
			    bool aggressive = Rnd.chance(actor.getParameter("SelfAggressive", actor.isAggressive() ? 100 : 0));
			    if (!actor.getAggroList().isEmpty() || aggressive)
			    {
				    List chars = World.getAroundCharacters(actor);
				    CollectionUtils.eqSort(chars, _nearestTargetComparator);
				    if ((from Creature cha in (IEnumerable)chars where aggressive || (actor.getAggroList().get(cha) != null) select cha).Any(cha => checkAggression(cha)))
				    {
				        return true;
				    }
			    }
		    }
		    if (actor.isMinion())
		    {
			    MonsterInstance leader = ((MinionInstance) actor).getLeader();
			    if (leader != null)
			    {
				    double distance = actor.getDistance(leader.getX(), leader.getY());
				    if (distance > 1000)
				    {
					    actor.teleToLocation(leader.getMinionPosition());
				    }
				    else if (distance > 200)
				    {
					    addTaskMove(leader.getMinionPosition(), false);
				    }
				    return true;
			    }
		    }
		    return randomAnimation() || randomWalk();
	    }
        protected override void onIntentionIdle()
        {
            NpcInstance actor = getActor();
            clearTasks();
            actor.stopMove();
            actor.getAggroList().clear(true);
            setAttackTimeout(Long.MAX_VALUE);
            setAttackTarget(null);
            changeIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
        }
        protected override void onIntentionActive()
        {
            NpcInstance actor = getActor();
            actor.stopMove();
            setAttackTimeout(Long.MAX_VALUE);
            if (getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)
            {
                switchAITask(AI_TASK_ACTIVE_DELAY);
                changeIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
            }
            onEvtThink();
        }
        protected override void onIntentionAttack(Creature target)
        {
            NpcInstance actor = getActor();
            clearTasks();
            actor.stopMove();
            setAttackTarget(target);
            setAttackTimeout(getMaxAttackTimeout() + java.lang.System.currentTimeMillis());
            setGlobalAggro(0);
            if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
            {
                changeIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
                switchAITask(AI_TASK_ATTACK_DELAY);
            }
            onEvtThink();
        }
        protected virtual bool canAttackCharacter(Creature target)
        {
            return target.isPlayable();
        }
        protected bool checkTarget(Creature target, int range)
	    {
		    NpcInstance actor = getActor();
		    if ((target == null) || target.isAlikeDead() || !actor.isInRangeZ(target, range))
		    {
			    return false;
		    }
		    var hided = target.isPlayable() && !canSeeInHide((Playable) target);
		    if (!hided && actor.isConfused())
		    {
			    return true;
		    }
		    if (getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
		    {
			    AggroList.AggroInfo ai = actor.getAggroList().get(target);
			    if (ai != null)
			    {
				    if (hided)
				    {
					    ai.hate = 0;
					    return false;
				    }
				    return ai.hate > 0;
			    }
			    return false;
		    }
		    return canAttackCharacter(target);
	    }
        public void setAttackTimeout(long time)
        {
            _attackTimeout = time;
        }
        protected long getAttackTimeout()
        {
            return _attackTimeout;
        }
        protected void thinkAttack(bool _returnHome)
        {
            NpcInstance actor = getActor();
            if (actor.isDead())
            {
                return;
            }
            if (!doTask() || actor.isAttackingNow() || actor.isCastingNow()) return;
            if (createNewTask()) return;
            if (java.lang.System.currentTimeMillis() > getAttackTimeout())
            {
                returnHome();
            }
        }
        protected void thinkAttack()
        {
            NpcInstance actor = getActor();
            if (actor.isDead())
            {
                return;
            }
            Location loc = actor.getSpawnedLoc();
            if (!actor.isInRange(loc, MAX_PURSUE_RANGE))
            {
                teleportHome();
                return;
            }
            if (doTask() && !actor.isAttackingNow() && !actor.isCastingNow())
            {
                if (!createNewTask())
                {
                    if (java.lang.System.currentTimeMillis() > getAttackTimeout())
                    {
                        returnHome();
                    }
                }
            }
        }
        protected override void onEvtSpawn()
        {
            setGlobalAggro(java.lang.System.currentTimeMillis() + getActor().getParameter("globalAggro", 10000L));
            setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
        }
        protected override void onEvtReadyToAct()
        {
            onEvtThink();
        }
        protected override void onEvtArrivedTarget()
        {
            onEvtThink();
        }
        protected override void onEvtArrived()
        {
            onEvtThink();
        }
        protected bool tryMoveToTarget(Creature target)
        {
            return tryMoveToTarget(target, 0);
        }
        protected bool tryMoveToTarget(Creature target, int range)
        {
            NpcInstance actor = getActor();
            if (!actor.followToCharacter(target, actor.getPhysicalAttackRange(), true))
            {
                _pathfindFails++;
            }
            if ((_pathfindFails >= getMaxPathfindFails()) && (java.lang.System.currentTimeMillis() > ((getAttackTimeout() - getMaxAttackTimeout()) + getTeleportTimeout())) && actor.isInRange(target, MAX_PURSUE_RANGE))
            {
                _pathfindFails = 0;
                if (target.isPlayable())
                {
                    AggroList.AggroInfo hate = actor.getAggroList().get(target);
                    if ((hate == null) || (hate.hate < 100))
                    {
                        returnHome();
                        return false;
                    }
                }
                Location loc = GeoEngine.moveCheckForAI(target.getLoc(), actor.getLoc(), actor.getGeoIndex());
                if (!GeoEngine.canMoveToCoord(actor.getX(), actor.getY(), actor.getZ(), loc.x, loc.y, loc.z, actor.getGeoIndex()))
                {
                    loc = target.getLoc();
                }
                actor.teleToLocation(loc);
            }
            return true;
        }
        protected bool maybeNextTask(Task currentTask)
        {
            _tasks.remove(currentTask);
            if (_tasks.size() == 0)
            {
                return true;
            }
            return false;
        }
        protected bool doTask()
        {
            NpcInstance actor = getActor();
            if (!_def_think)
            {
                return true;
            }
            Task currentTask = (Task) _tasks.pollFirst();
            if (currentTask == null)
            {
                clearTasks();
                return true;
            }
            if (actor.isDead() || actor.isAttackingNow() || actor.isCastingNow())
            {
                return false;
            }
            switch (currentTask.type)
            {
                case TaskType.MOVE:
                    {
                        if (actor.isMovementDisabled() || !getIsMobile())
                        {
                            return true;
                        }
                        if (actor.isInRange(currentTask.loc, 100))
                        {
                            return maybeNextTask(currentTask);
                        }
                        if (actor.isMoving)
                        {
                            return false;
                        }
                        if (!actor.moveToLocation(currentTask.loc, 0, currentTask.pathfind))
                        {
                            clientStopMoving();
                            _pathfindFails = 0;
                            actor.teleToLocation(currentTask.loc);
                            return maybeNextTask(currentTask);
                        }
                    }
                    break;
                case TaskType.ATTACK:
                    {
                        Creature target = currentTask.target.get();
                        if (!checkTarget(target, MAX_PURSUE_RANGE))
                        {
                            return true;
                        }
                        setAttackTarget(target);
                        if (actor.isMoving)
                        {
                            return Rnd.chance(25);
                        }
                        if ((actor.getRealDistance3D(target) <= (actor.getPhysicalAttackRange() + 40)) && GeoEngine.canSeeTarget(actor, target, false))
                        {
                            clientStopMoving();
                            _pathfindFails = 0;
                            setAttackTimeout(getMaxAttackTimeout() + java.lang.System.currentTimeMillis());
                            actor.doAttack(target);
                            return maybeNextTask(currentTask);
                        }
                        if (actor.isMovementDisabled() || !getIsMobile())
                        {
                            return true;
                        }
                        tryMoveToTarget(target);
                    }
                    break;
                case TaskType.CAST:
                    {
                        Creature target = currentTask.target.get();
                        if (actor.isMuted(currentTask.skill) || actor.isSkillDisabled(currentTask.skill) || actor.isUnActiveSkill(currentTask.skill.getId()))
                        {
                            return true;
                        }
                        bool isAoE = currentTask.skill.getTargetType() == Skill.SkillTargetType.TARGET_AURA;
                        int castRange = currentTask.skill.getAOECastRange();
                        if (!checkTarget(target, MAX_PURSUE_RANGE + castRange))
                        {
                            return true;
                        }
                        setAttackTarget(target);
                        if ((actor.getRealDistance3D(target) <= (castRange + 60)) && GeoEngine.canSeeTarget(actor, target, false))
                        {
                            clientStopMoving();
                            _pathfindFails = 0;
                            setAttackTimeout(getMaxAttackTimeout() + java.lang.System.currentTimeMillis());
                            actor.doCast(currentTask.skill, isAoE ? actor : target, !target.isPlayable());
                            return maybeNextTask(currentTask);
                        }
                        if (actor.isMoving)
                        {
                            return Rnd.chance(10);
                        }
                        if (actor.isMovementDisabled() || !getIsMobile())
                        {
                            return true;
                        }
                        tryMoveToTarget(target, castRange);
                    }
                    break;
                case TaskType.BUFF:
                    {
                        Creature target = currentTask.target.get();
                        if (actor.isMuted(currentTask.skill) || actor.isSkillDisabled(currentTask.skill) || actor.isUnActiveSkill(currentTask.skill.getId()))
                        {
                            return true;
                        }
                        if ((target == null) || target.isAlikeDead() || !actor.isInRange(target, 2000))
                        {
                            return true;
                        }
                        bool isAoE = currentTask.skill.getTargetType() == Skill.SkillTargetType.TARGET_AURA;
                        int castRange = currentTask.skill.getAOECastRange();
                        if (actor.isMoving)
                        {
                            return Rnd.chance(10);
                        }
                        if ((actor.getRealDistance3D(target) <= (castRange + 60)) && GeoEngine.canSeeTarget(actor, target, false))
                        {
                            clientStopMoving();
                            _pathfindFails = 0;
                            actor.doCast(currentTask.skill, isAoE ? actor : target, !target.isPlayable());
                            return maybeNextTask(currentTask);
                        }
                        if (actor.isMovementDisabled() || !getIsMobile())
                        {
                            return true;
                        }
                        tryMoveToTarget(target);
                    }
                    break;
            }
            return false;
        }
        protected virtual bool createNewTask()
        {
            return false;
        }
        protected bool defaultNewTask()
        {
            clearTasks();
            NpcInstance actor = getActor();
            Creature target;
            if ((actor == null) || ((target = prepareTarget()) == null))
            {
                return false;
            }
            double distance = actor.getDistance(target);
            return chooseTaskAndTargets(null, target, distance);
        }
        protected override void onEvtThink()
        {
            NpcInstance actor = getActor();
            if (_thinking || (actor == null) || actor.isActionsDisabled() || actor.isAfraid())
            {
                return;
            }
            if (_randomAnimationEnd > java.lang.System.currentTimeMillis())
            {
                return;
            }
            if (actor.isRaid() && (actor.isInZonePeace() || actor.isInZoneBattle() || actor.isInZone(ZoneType.SIEGE)))
            {
                teleportHome();
                return;
            }
            _thinking = true;
            try
            {
                if (!Config.BLOCK_ACTIVE_TASKS && (getIntention() == CtrlIntention.AI_INTENTION_ACTIVE))
                {
                    thinkActive();
                }
                else if (getIntention() == CtrlIntention.AI_INTENTION_ATTACK)
                {
                    thinkAttack();
                }
            }
            finally
            {
                _thinking = false;
            }
        }
        protected override void onEvtDead(Creature killer)
        {
            NpcInstance actor = getActor();
            int transformer = actor.getParameter("transformOnDead", 0);
            int chance = actor.getParameter("transformChance", 100);
            if ((transformer > 0) && Rnd.chance(chance))
            {
                NpcInstance npc = NpcUtils.spawnSingle(transformer, actor.getLoc(), actor.getReflection());
                if ((killer != null) && killer.isPlayable())
                {
                    npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, killer, 100);
                    killer.setTarget(npc);
                    killer.sendPacket(new StatusUpdate(npc).addAttribute(StatusUpdateField.CUR_HP, StatusUpdateField.MAX_HP));
                }
            }
            base.onEvtDead(killer);
        }
        protected override void onEvtClanAttacked(Creature attacked, Creature attacker, int damage)
        {
            if ((getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) || !isGlobalAggro())
            {
                return;
            }
            notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, 2);
        }
        protected override void onEvtAttacked(Creature attacker, int damage)
	    {
		    NpcInstance actor = getActor();
		    if ((attacker == null) || actor.isDead())
		    {
			    return;
		    }
		    int transformer = actor.getParameter("transformOnUnderAttack", 0);
		    if (transformer > 0)
		    {
			    int chance = actor.getParameter("transformChance", 5);
			    if ((chance == 100) || ((((MonsterInstance) actor).getChampion() == 0) && (actor.getCurrentHpPercents() > 50) && Rnd.chance(chance)))
			    {
				    MonsterInstance npc = (MonsterInstance) NpcHolder.getInstance().getTemplate(transformer).getNewInstance();
				    npc.setSpawnedLoc(actor.getLoc());
				    npc.setReflection(actor.getReflection());
				    npc.setChampion(((MonsterInstance) actor).getChampion());
				    npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp(), true);
				    npc.spawnMe(npc.getSpawnedLoc());
				    npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, 100);
				    actor.doDie(actor);
				    actor.decayMe();
				    attacker.setTarget(npc);
				    attacker.sendPacket(new StatusUpdate(npc).addAttribute(StatusUpdateField.CUR_HP, StatusUpdateField.MAX_HP));
				    return;
			    }
		    }
		    var player = attacker.getPlayer();
		    if (player != null)
		    {
			    List quests = player.getQuestsForEvent(actor, QuestEventType.ATTACKED_WITH_QUEST, false);
			    if (quests != null)
			    {
				    foreach (QuestState qs in (IEnumerable)quests)
				    {
					    qs.getQuest().notifyAttack(actor, qs);
				    }
			    }
		    }
		    actor.getAggroList().addDamageHate(attacker, 0, damage);
		    if ((damage > 0) && (attacker.isServitor() || attacker.isPet()))
		    {
			    actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? damage : 1);
		    }
		    if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
		    {
			    if (!actor.isRunning())
			    {
				    startRunningTask(AI_TASK_ATTACK_DELAY);
			    }
			    setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
		    }
		    notifyFriends(attacker, damage);
	    }
        protected override void onEvtAggression(Creature attacker, int aggro)
        {
            NpcInstance actor = getActor();
            if ((attacker == null) || actor.isDead())
            {
                return;
            }
            actor.getAggroList().addDamageHate(attacker, 0, aggro);
            if ((aggro > 0) && (attacker.isServitor() || attacker.isPet()))
            {
                actor.getAggroList().addDamageHate(attacker.getPlayer(), 0, actor.getParameter("searchingMaster", false) ? aggro : 1);
            }
            if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
            {
                if (!actor.isRunning())
                {
                    startRunningTask(AI_TASK_ATTACK_DELAY);
                }
                setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
            }
        }
        protected bool maybeMoveToHome()
        {
            NpcInstance actor = getActor();
            if (actor.isDead())
            {
                return false;
            }
            bool randomWalk = actor.hasRandomWalk();
            Location sloc = actor.getSpawnedLoc();
            if (randomWalk && (!Config.RND_WALK || !Rnd.chance(Config.RND_WALK_RATE)))
            {
                return false;
            }
            bool isInRange = actor.isInRangeZ(sloc, Config.MAX_DRIFT_RANGE);
            if (!randomWalk && isInRange)
            {
                return false;
            }
            Location pos = Location.findPointToStay(actor, sloc, 0, Config.MAX_DRIFT_RANGE);
            actor.setWalking();
            if (!actor.moveToLocation(pos.x, pos.y, pos.z, 0, true) && !isInRange)
            {
                teleportHome();
            }
            return true;
        }
        protected void returnHome()
        {
            returnHome(true, Config.ALWAYS_TELEPORT_HOME);
        }
        protected void teleportHome()
        {
            returnHome(true, true);
        }
        protected void returnHome(bool clearAggro, bool teleport)
        {
            NpcInstance actor = getActor();
            Location sloc = actor.getSpawnedLoc();
            clearTasks();
            actor.stopMove();
            if (clearAggro)
            {
                actor.getAggroList().clear(true);
            }
            setAttackTimeout(Long.MAX_VALUE);
            setAttackTarget(null);
            changeIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
            if (teleport)
            {
                actor.broadcastPacketToOthers(new MagicSkillUse(actor, actor, 2036, 1, 500, 0));
                actor.teleToLocation(sloc.x, sloc.y, GeoEngine.getHeight(sloc, actor.getGeoIndex()));
            }
            else
            {
                if (!clearAggro)
                {
                    actor.setRunning();
                }
                else
                {
                    actor.setWalking();
                }
                addTaskMove(sloc, false);
            }
        }
        protected Creature prepareTarget()
	    {
		    NpcInstance actor = getActor();
		    if (actor.isConfused())
		    {
			    return getAttackTarget();
		    }
		    if (Rnd.chance(actor.getParameter("isMadness", 0)))
		    {
			    Creature randomHated = actor.getAggroList().getRandomHated();
			    if (randomHated != null)
			    {
				    setAttackTarget(randomHated);
				    if ((_madnessTask == null) && !actor.isConfused())
				    {
					    actor.startConfused();
					    _madnessTask = ThreadPoolManager.getInstance().schedule(new MadnessTask(), 10000);
				    }
				    return randomHated;
			    }
		    }
		    List hateList = actor.getAggroList().getHateList();
		    Creature hated = null;
		    foreach (Creature cha in (IEnumerable)hateList)
		    {
			    if (!checkTarget(cha, MAX_PURSUE_RANGE))
			    {
				    actor.getAggroList().remove(cha, true);
				    continue;
			    }
			    hated = cha;
			    break;
		    }
		    if (hated != null)
		    {
			    setAttackTarget(hated);
			    return hated;
		    }
		    return null;
	    }
        protected bool canUseSkill(Skill skill, Creature target, double distance)
        {
            NpcInstance actor = getActor();
            if ((skill == null) || skill.isNotUsedByAI())
            {
                return false;
            }
            if ((skill.getTargetType() == Skill.SkillTargetType.TARGET_SELF) && (target != actor))
            {
                return false;
            }
            int castRange = skill.getAOECastRange();
            if ((castRange <= 200) && (distance > 200))
            {
                return false;
            }
            if (actor.isSkillDisabled(skill) || actor.isMuted(skill) || actor.isUnActiveSkill(skill.getId()))
            {
                return false;
            }
            double mpConsume2 = skill.getMpConsume2();
            if (skill.isMagic())
            {
                mpConsume2 = actor.calcStat(Stats.MP_MAGIC_SKILL_CONSUME, mpConsume2, target, skill);
            }
            else
            {
                mpConsume2 = actor.calcStat(Stats.MP_PHYSICAL_SKILL_CONSUME, mpConsume2, target, skill);
            }
            if (actor.getCurrentMp() < mpConsume2)
            {
                return false;
            }
            if (target.getEffectList().getEffectsCountForSkill(skill.getId()) != 0)
            {
                return false;
            }
            return true;
        }
        protected bool canUseSkill(Skill sk, Creature target)
        {
            return canUseSkill(sk, target, 0);
        }
        protected Skill[] selectUsableSkills(Creature target, double distance, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0) || (target == null))
		    {
			    return null;
		    }
		    Skill[] ret = null;
		    int usable = 0;
		    foreach (Skill skill in skills.Where(skill => canUseSkill(skill, target, distance)))
		    {
		        if (ret == null)
		        {
		            ret = new Skill[skills.Length];
		        }
		        ret[usable++] = skill;
		    }
		    if ((ret == null) || (usable == skills.Length))
		    {
			    return ret;
		    }
		    if (usable == 0)
		    {
			    return null;
		    }
		    ret = Arrays.copyOf(ret, usable) as Skill[];
		    return ret;
	    }
        protected static Skill selectTopSkillByDamage(Creature actor, Creature target, double distance, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0))
		    {
			    return null;
		    }
		    if (skills.Length == 1)
		    {
			    return skills[0];
		    }
		    var rnd = new RndSelector(skills.Length);
            foreach (Skill skill in skills)
		    {
			    double weight = (skill.getSimpleDamage(actor, target) * skill.getAOECastRange()) / distance;
			    if (weight < 1.0)
			    {
				    weight = 1.0;
			    }
			    rnd.add(skill, (int) weight);
		    }
		    return rnd.@select() as Skill;
	    }
        protected static Skill selectTopSkillByDebuff(Creature actor, Creature target, double distance, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0))
		    {
			    return null;
		    }
		    if (skills.Length == 1)
		    {
			    return skills[0];
		    }
		    var rnd = new RndSelector(skills.Length);
            foreach (Skill skill in skills)
		    {
			    if (skill.getSameByStackType(target) != null)
			    {
				    continue;
			    }
		        double weight;
		        if ((weight = (100.0 * skill.getAOECastRange()) / distance) <= 0)
			    {
				    weight = 1;
			    }
			    rnd.add(skill, (int) weight);
		    }
		    return rnd.@select() as Skill;
	    }
        protected static Skill selectTopSkillByBuff(Creature target, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0))
		    {
			    return null;
		    }
		    if (skills.Length == 1)
		    {
			    return skills[0];
		    }
		    var rnd = new RndSelector(skills.Length);
            foreach (Skill skill in skills.Where(skill => skill.getSameByStackType(target) == null))
            {
                double weight;
                if ((weight = skill.getPower()) <= 0)
                {
                    weight = 1;
                }
                rnd.add(skill, (int) weight);
            }
		    return rnd.@select() as Skill;
	    }
        protected static Skill selectTopSkillByHeal(Creature target, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0))
		    {
			    return null;
		    }
		    double hpReduced = target.getMaxHp() - target.getCurrentHp();
		    if (hpReduced < 1)
		    {
			    return null;
		    }
		    if (skills.Length == 1)
		    {
			    return skills[0];
		    }
		    var rnd = new RndSelector(skills.Length);
            foreach (Skill skill in skills)
		    {
		        double weight;
		        if ((weight = Math.abs(skill.getPower() - hpReduced)) <= 0)
			    {
				    weight = 1;
			    }
			    rnd.add(skill, (int) weight);
		    }
		    return rnd.@select() as Skill;
	    }
        protected void addDesiredSkill(Map skillMap, Creature target, double distance, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0) || (target == null))
		    {
			    return;
		    }
		    foreach (Skill sk in skills)
		    {
			    addDesiredSkill(skillMap, target, distance, sk);
		    }
	    }
        protected void addDesiredSkill(Map skillMap, Creature target, double distance, Skill skill)
        {
            if ((skill == null) || (target == null) || !canUseSkill(skill, target))
            {
                return;
            }
            var weight = (int)-Math.abs(skill.getAOECastRange() - distance);
            if (skill.getAOECastRange() >= distance)
            {
                weight += 1000000;
            }
            else if (skill.isNotTargetAoE() && (skill.getTargets(getActor(), target, false).size() == 0))
            {
                return;
            }
            skillMap.put(skill, weight);
        }
        protected void addDesiredHeal(Map skillMap, Skill[] skills)
	    {
		    if ((skills == null) || (skills.length == 0))
		    {
			    return;
		    }
		    NpcInstance actor = getActor();
		    double hpReduced = actor.getMaxHp() - actor.getCurrentHp();
		    double hpPercent = actor.getCurrentHpPercents();
		    if (hpReduced < 1)
		    {
			    return;
		    }
            foreach (Skill sk in skills)
		    {
		        if (!canUseSkill(sk, actor) || (!(sk.getPower() <= hpReduced))) continue;
		        var weight = (int) sk.getPower();
		        if (hpPercent < 50)
		        {
		            weight += 1000000;
		        }
		        skillMap.put(sk, weight);
		    }
	    }
        protected void addDesiredBuff(Map skillMap, Skill[] skills)
	    {
		    if ((skills == null) || (skills.Length == 0))
		    {
			    return;
		    }
		    NpcInstance actor = getActor();
		    foreach (Skill sk in skills)
		    {
			    if (canUseSkill(sk, actor))
			    {
				    skillMap.put(sk, 1000000);
			    }
		    }
	    }
        protected Skill selectTopSkill(Map skillMap)
	    {
		    if ((skillMap == null) || skillMap.isEmpty())
		    {
			    return null;
		    }
		    int nWeight, topWeight = Integer.MIN_VALUE;
		    foreach (Skill next in (IEnumerable)skillMap.keySet())
		    {
			    if ((nWeight = (int) skillMap.get(next)) > topWeight)
			    {
				    topWeight = nWeight;
			    }
		    }
		    if (topWeight == Integer.MIN_VALUE)
		    {
			    return null;
		    }
		    var skills = new Skill[skillMap.size()];
		    nWeight = 0;
		    foreach (Map.Entry e in from Map.Entry e in (IEnumerable)skillMap.entrySet() where (int)e.getValue() >= topWeight select e)
		    {
		        skills[nWeight++] = e.getKey() as Skill;
		    }
		    return skills[Rnd.get(nWeight)];
	    }
        protected bool chooseTaskAndTargets(Skill skill, Creature target, double distance)
	    {
		    NpcInstance actor = getActor();
		    if (skill != null)
		    {
			    if (actor.isMovementDisabled() && (distance > (skill.getAOECastRange() + 60)))
			    {
				    target = null;
				    if (skill.isOffensive())
				    {
					    LazyArrayList targets = LazyArrayList.newInstance();
					    foreach (Creature cha in actor.getAggroList().getHateList())
					    {
						    if (!checkTarget(cha, skill.getAOECastRange() + 60) || !canUseSkill(skill, cha))
						    {
							    continue;
						    }
						    targets.add(cha);
					    }
					    if (!targets.isEmpty())
					    {
						    target = targets.get(Rnd.get(targets.size())) as Creature;
					    }
					    LazyArrayList.recycle(targets);
				    }
			    }
			    if (target == null)
			    {
				    return false;
			    }
			    if (skill.isOffensive())
			    {
				    addTaskCast(target, skill);
			    }
			    else
			    {
				    addTaskBuff(target, skill);
			    }
			    return true;
		    }
		    if (actor.isMovementDisabled() && (distance > (actor.getPhysicalAttackRange() + 40)))
		    {
			    target = null;
			    LazyArrayList targets = LazyArrayList.newInstance();
			    foreach (Creature cha in actor.getAggroList().getHateList())
			    {
				    if (!checkTarget(cha, actor.getPhysicalAttackRange() + 40))
				    {
					    continue;
				    }
				    targets.add(cha);
			    }
			    if (!targets.isEmpty())
			    {
				    target = targets.get(Rnd.get(targets.size())) as Creature;
			    }
			    LazyArrayList.recycle(targets);
		    }
		    if (target == null)
		    {
			    return false;
		    }
		    addTaskAttack(target);
		    return true;
	    }
        public override bool isActive()
        {
            return _aiTask != null;
        }
        protected void clearTasks()
        {
            _def_think = false;
            _tasks.clear();
        }
        protected void startRunningTask(long interval)
        {
            NpcInstance actor = getActor();
            if ((actor != null) && (_runningTask == null) && !actor.isRunning())
            {
                _runningTask = ThreadPoolManager.getInstance().schedule(new RunningTask(), interval);
            }
        }
        protected bool isGlobalAggro()
        {
            if (_globalAggro == 0)
            {
                return true;
            }
            if (_globalAggro <= java.lang.System.currentTimeMillis())
            {
                _globalAggro = 0;
                return true;
            }
            return false;
        }
        public void setGlobalAggro(long value)
        {
            _globalAggro = value;
        }
        public override NpcInstance getActor()
        {
            return (NpcInstance)base.getActor();
        }
        protected bool defaultThinkBuff(int rateSelf)
        {
            return defaultThinkBuff(rateSelf, 0);
        }
        protected void notifyFriends(Creature attacker, int damage)
	    {
		    NpcInstance actor = getActor();
		    if ((java.lang.System.currentTimeMillis() - _lastFactionNotifyTime) > _minFactionNotifyInterval)
		    {
			    _lastFactionNotifyTime = java.lang.System.currentTimeMillis();
		        MinionList minionList;
		        if (actor.isMinion())
			    {
				    MonsterInstance master = ((MinionInstance) actor).getLeader();
				    if (master != null)
				    {
					    if (!master.isDead() && master.isVisible())
					    {
						    master.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage);
					    }
					    minionList = master.getMinionList();
					    if (minionList != null)
					    {
						    foreach (MinionInstance minion in minionList.getAliveMinions())
						    {
							    if (minion != actor)
							    {
								    minion.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage);
							    }
						    }
					    }
				    }
			    }
			    minionList = actor.getMinionList();
			    if ((minionList != null) && minionList.hasAliveMinions())
			    {
				    foreach (MinionInstance minion in minionList.getAliveMinions())
				    {
					    minion.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, attacker, damage);
				    }
			    }
			    foreach (NpcInstance npc in activeFactionTargets())
			    {
				    npc.getAI().notifyEvent(CtrlEvent.EVT_CLAN_ATTACKED, actor, attacker, damage);
			    }
		    }
	    }
        protected List activeFactionTargets()
	    {
		    NpcInstance actor = getActor();
		    if (actor.getFaction().isNone())
		    {
			    return Collections.emptyList();
		    }
		    List npcFriends = new LazyArrayList();
		    foreach (NpcInstance npc in World.getAroundNpc(actor))
		    {
			    if (!npc.isDead())
			    {
				    if (npc.isInFaction(actor))
				    {
					    if (npc.isInRangeZ(actor, npc.getFaction().getRange()))
					    {
						    if (GeoEngine.canSeeTarget(npc, actor, false))
						    {
							    npcFriends.add(npc);
						    }
					    }
				    }
			    }
		    }
		    return npcFriends;
	    }
        protected bool defaultThinkBuff(int rateSelf, int rateFriends)
	    {
		    NpcInstance actor = getActor();
		    if (actor.isDead())
		    {
			    return true;
		    }
		    if (Rnd.chance(rateSelf))
		    {
			    double actorHp = actor.getCurrentHpPercents();
			    Skill[] skills = actorHp < 50 ? selectUsableSkills(actor, 0, _healSkills) : selectUsableSkills(actor, 0, _buffSkills);
			    if ((skills == null) || (skills.Length == 0))
			    {
				    return false;
			    }
			    Skill skill = skills[Rnd.get(skills.Length)];
			    addTaskBuff(actor, skill);
			    return true;
		    }
		    if (Rnd.chance(rateFriends))
		    {
			    foreach (NpcInstance npc in (IEnumerable)activeFactionTargets())
			    {
				    double targetHp = npc.getCurrentHpPercents();
				    Skill[] skills = targetHp < 50 ? selectUsableSkills(actor, 0, _healSkills) : selectUsableSkills(actor, 0, _buffSkills);
				    if ((skills == null) || (skills.Length == 0))
				    {
					    continue;
				    }
				    Skill skill = skills[Rnd.get(skills.Length)];
				    addTaskBuff(actor, skill);
				    return true;
			    }
		    }
		    return false;
	    }
        protected bool defaultFightTask()
	    {
		    clearTasks();
		    NpcInstance actor = getActor();
		    if (actor.isDead() || actor.isAMuted())
		    {
			    return false;
		    }
		    Creature target;
		    if ((target = prepareTarget()) == null)
		    {
			    return false;
		    }
		    double distance = actor.getDistance(target);
		    double targetHp = target.getCurrentHpPercents();
		    double actorHp = actor.getCurrentHpPercents();
		    Skill[] dam = Rnd.chance(getRateDAM()) ? selectUsableSkills(target, distance, _damSkills) : null;
		    Skill[] dot = Rnd.chance(getRateDOT()) ? selectUsableSkills(target, distance, _dotSkills) : null;
		    Skill[] debuff = targetHp > 10 ? Rnd.chance(getRateDEBUFF()) ? selectUsableSkills(target, distance, _debuffSkills) : null : null;
		    Skill[] stun = Rnd.chance(getRateSTUN()) ? selectUsableSkills(target, distance, _stunSkills) : null;
		    Skill[] heal = actorHp < 50 ? Rnd.chance(getRateHEAL()) ? selectUsableSkills(actor, 0, _healSkills) : null : null;
		    Skill[] buff = Rnd.chance(getRateBUFF()) ? selectUsableSkills(actor, 0, _buffSkills) : null;
		    RndSelector rnd = new RndSelector();
		    if (!actor.isAMuted())
		    {
			    rnd.add(null, getRatePHYS());
		    }
		    rnd.add(dam, getRateDAM());
		    rnd.add(dot, getRateDOT());
		    rnd.add(debuff, getRateDEBUFF());
		    rnd.add(heal, getRateHEAL());
		    rnd.add(buff, getRateBUFF());
		    rnd.add(stun, getRateSTUN());
		    Skill[] selected = rnd.@select() as Skill[];
		    if (selected != null)
		    {
			    if ((selected == dam) || (selected == dot))
			    {
				    return chooseTaskAndTargets(selectTopSkillByDamage(actor, target, distance, selected), target, distance);
			    }
			    if ((selected == debuff) || (selected == stun))
			    {
				    return chooseTaskAndTargets(selectTopSkillByDebuff(actor, target, distance, selected), target, distance);
			    }
			    if (selected == buff)
			    {
				    return chooseTaskAndTargets(selectTopSkillByBuff(actor, selected), actor, distance);
			    }
			    if (selected == heal)
			    {
				    return chooseTaskAndTargets(selectTopSkillByHeal(actor, selected), actor, distance);
			    }
		    }
		    return chooseTaskAndTargets(null, target, distance);
	    }
        public virtual int getRatePHYS()
        {
            return 100;
        }
        public virtual int getRateDOT()
        {
            return 0;
        }
        public virtual int getRateDEBUFF()
        {
            return 0;
        }
        public virtual int getRateDAM()
        {
            return 0;
        }
        public virtual int getRateSTUN()
        {
            return 0;
        }
        public virtual int getRateBUFF()
        {
            return 0;
        }
        public virtual int getRateHEAL()
        {
            return 0;
        }
        public bool getIsMobile()
        {
            return !getActor().getParameter("isImmobilized", false);
        }
        public int getMaxPathfindFails()
        {
            return 3;
        }
        public virtual int getMaxAttackTimeout()
        {
            return 15000;
        }
        public int getTeleportTimeout()
        {
            return 10000;
        }
    }
}
